﻿#pragma once
#include "yzrutil.h"
#include "DSP.h"
#include "DSPUtil.h"
#include "RingBuffer.h"
#include "AllPassFilter.h"
#include "BufferDelayer.h"
#include "LowPassFeedbackCombFilter.h"
#include "AmpAdd.h"

namespace yzrilyzr_dsp{
	ECLASS(Freeverb, public DSP){
	private:
	RingBuffer<u_sample> sumBuf1;
	u_sample dcOffset=0;
	public:
	LowPassFeedbackCombFilter combs[8];
	AllPassFilter apfs[4];
	u_normal_01 wetRatio=0;
	u_normal_01 roomSize=0.84;
	u_normal_01 damper=0.2;
	BufferDelayer::DelayFunction delayFunction=BufferDelayer::linearDelay;
	public:
	Freeverb();
	Freeverb(u_normal_01 wetRatio, u_normal_01 roomSize=0.84, u_normal_01 damper=0.2) :wetRatio(wetRatio), roomSize(roomSize), damper(damper){}
	u_sample procDsp(u_sample input) override{
		if(wetRatio == 0 || roomSize == 0)return input;
		u_sample dcSmooth=0.995;
		dcOffset=dcSmooth * dcOffset + (1 - dcSmooth) * input;
		input-=dcOffset;
		u_sample sum=0;
		for(u_index i=0;i < 8;i++){
			sum+=combs[i].procDsp(input);
		}
		for(u_index i=0;i < 4;i++){
			sum=apfs[i].procDsp(sum);
		}
		return input * (1 - wetRatio) + sum * 0.04 * wetRatio;
	}
	void procBlock(u_sample * input, u_sample * output, u_index length)override{
		if(wetRatio == 0 || roomSize == 0)return;
		sumBuf1.ensureCapacity(length);
		sumBuf1.fill(0);
		u_sample dcSmooth=0.995;
		for(u_index i=0;i < length;i++){
			dcOffset=dcSmooth * dcOffset + (1 - dcSmooth) * input[i];
			input[i]-=dcOffset;
		}
		for(u_index i=0;i < 8;i++){
			auto & comb=combs[i];
			for(u_index j=0;j < length;j++){
				sumBuf1.buffer[j]+=comb.procDsp(input[j]);
			}
		}
		for(u_index i=0;i < 4;i++){
			apfs[i].procBlock(sumBuf1.buffer, length);
		}
		for(u_index i=0;i < length;i++){
			output[i]=input[i] * (1 - wetRatio) + sumBuf1.buffer[i] * 0.04 * wetRatio;
		}
	}
	void init(u_sample_rate sampleRate) override{
		u_sample a=1000.0 / (u_sample)sampleRate;
		static u_sample apfsDelay[4]={225.0, 556.0, 441.0, 341.0};
		static u_sample combsDelay[8]={1617, 1557, 1491, 1422, 1356, 1277, 1188, 1116};
		for(u_index i=0;i < 4;i++){
			apfs[i].delayTimeMs=a * apfsDelay[i];
			apfs[i].gain=0.5;
			apfs[i].delayFunction=delayFunction;
			apfs[i].init(sampleRate);
		}
		for(u_index i=0;i < 8;i++){
			combs[i].delayTimeMs=a * combsDelay[i];
			combs[i].roomSize=roomSize;
			combs[i].damper=damper;
			combs[i].delayFunction=delayFunction;
			combs[i].init(sampleRate);
		}
	}
	void resetMemory() override{
		for(u_index i=0;i < 8;i++){
			combs[i].resetMemory();
		}
		for(u_index i=0;i < 4;i++){
			apfs[i].resetMemory();
		}
	}
	std::shared_ptr<DSP> cloneDSP() override{
		return std::make_shared < Freeverb>(wetRatio);
	}
	void cloneParam(DSP * obj1)override{
		if(auto other=dynamic_cast<Freeverb *>(obj1)){
			wetRatio=other->wetRatio;
			roomSize=other->roomSize;
			damper=other->damper;
			delayFunction=other->delayFunction;
		}
	}
	yzrilyzr_lang::String toString()const override;
	};
}
